﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


using Effects.Effects;
using Effects.Effects.GuiParts;

namespace EffectsPack
{
    // Pasive volume box. Volume can be set in 0-1 range.
    /// <summary>
    ///  Pasive volume control. 
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   Passive means that this effects can only attenuate input signal but not amplify it.
    ///   This implementation can be used as a guide for implementing effects. Therefore it is well comented.
    ///  </para>
    /// </remarks>
    public class Volume:Effect
    {
        #region controls
        // Graphic controls that allows user cotrol behaviour of the effect

        #region footswitch
        // switch for turning the effect ON or OFF

        /// <summary>
        ///  Footswitch for turning the effect On/Off.
        /// </summary>
        private Footswitch footswitch;

        /// <summary>
        ///  Key for the footswitch setting.
        /// </summary>
        /// <remarks>
        ///  Key that identifies footswitch setting. This key will be later used in methods that handle recording changes in parameter settings.
        ///  Remeber that every key must be unique.
        /// </remarks>
        private const int footswitchKey = 0;

        /// <summary>
        ///  Indicates On/Off status of the effect.
        /// </summary>
        private LED led;

        /// <summary>
        ///  Synchornizes led with footswitch.
        /// </summary>
        /// <remarks>
        ///  Useful class that keeps synchornized footswitch and Led for us.
        /// </remarks>
        private FootswitchLedSynchronizer synchronizer;

        #endregion

        #region volume
        // control for volume level

        /// <summary>
        ///  Current attenuetion level from interval [0;1]. 
        ///  0 - no sound
        ///  1 - effects does nothing
        /// </summary>
        private double volume;

        /// <summary>
        ///  Controls attenuation level.
        /// </summary>
        /// <remarks>
        ///   Graphic control allows user set the volume.
        /// </remarks>
        private Pot VolumePot;


        /// <summary>
        ///  Key for volume level parameter.
        /// </summary>
        /// <remarks>
        ///  Rememeber that every key must be unique. Therefor this key differs from footswitchKey.
        /// </remarks>
        private const int volumeKey = 1;

        #endregion

        #endregion

        #region effects_properties
        /* Overriding necessary properties of the base class 
         * We keep DataSize and SampleRate properties as they are. For this effect we don't need to know the sampling rate or 
         * input array length. But in many cases there are necessary reaction on sampling rate change. */

        /// <summary>
        ///  Gets the name of the effect.
        /// </summary>
        /// <remarks>
        ///  This is unique identifier for the effect that should differ from all other effects.
        ///  We choose simple name of the effect. Suggested name might look like author-effect-version.
        /// </remarks>
        public override string Name
        {
            get{ return "Volume v1.0"; }
        }

        /// <summary>
        ///  Turns the effect On/Off.
        /// </summary>
        /// <remarks>
        ///  We override this property to keep it synchronized with LED and footswitch.
        ///  In fact it isn't necessary because the current engine doesn't use this property.
        /// </remarks>
        public override bool Switch
        {
            get
            {
                return base.Switch;
            }
            set
            {
                base.Switch = value;
                
                // synchronizer sets the value to the LED and footswitch
                synchronizer.SetValue(value);
            }
        }

        #endregion 

        #region constructor
        // Constructor and initialization stuff for the effect.

        /// <summary>
        ///  Creates new instance.
        /// </summary>
        /// <remarks>
        ///  Here can been seen one possible approach how to implement user interface of the effect. We simply doesn't bother with it :)
        ///  We just add all graphic controls and change background image. It is very simple, straigthforward and powerful.
        ///  Nevertheless one may want customized graphic interface, but then effect should somehow highlight the fact that it is selected (selected field is true).
        /// </remarks>
        public Volume()
        {
            this.BackgroundImage = System.Drawing.Image.FromStream(new System.IO.MemoryStream(EffectsPack.Properties.Resources.Volume));
            Size = BackgroundImage.Size;
            InititializeComponents();
            InitializeFootswitch();
        }

        /// <summary>
        ///  Initializes graphic controls.
        /// </summary>
        /// <remarks>
        ///  We create new pot, register event handler and add this pot to the controls
        /// </remarks>
        private void InititializeComponents()
        {
            VolumePot = new Pot();
            VolumePot.MaxValue = 1;
            VolumePot.MinValue = 0;
            VolumePot.ValueChanged += new PotValueChangedEventHandler(this.Volume_Moved);
            VolumePot.Knob=PotKnob.ChickenKnob;
            VolumePot.Location=new  System.Drawing.Point(20,50);
            Controls.Add(VolumePot);
            VolumePot.Reset();
        }

        /// <summary>
        ///  Initializes footswitch and led.
        /// </summary>
        private void InitializeFootswitch()
        {
            // footswitch
            footswitch = new Footswitch();
            switchedOn = true;
            footswitch.On = true;
            footswitch.Switched += new FootswitchSwitchedEventHandler(this.FootswitchSwitched);
            footswitch.Location = new System.Drawing.Point(110, 146);
            Controls.Add(footswitch);

            // led
            led = new LED();
            led.On = true;
            led.Location = new System.Drawing.Point(65, 30);
            Controls.Add(led);

            // synchronization between LED and footswitch
            synchronizer = new FootswitchLedSynchronizer(footswitch, led);
        }

        #endregion

        #region controls_event_handlers
        /* Handling events raised by graphic controls. 
         * In our situatuon it means by: volume potentiometer and footswitch */

        /// <summary>
        ///  Volume potentiometer event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        /// <remarks>
        ///  Needed action are very simple, we just adjust the field controling volume level and raised the SettingsChange event
        ///  so if anything is recording changes in settings it could react.
        /// </remarks>
        private void Volume_Moved(object sender, PotValueChangedEventArgs e)
        {
            volume = e.NewValue;

            // We prepare event arguments, and for the key we use volumeKey to identify the parameter/
            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(volumeKey, volume);
            if (SettingsChanged != null)
                SettingsChanged(this, changeArgs);

        }

        /// <summary>
        ///  Footswitch.Switched event handler. Turns effect On or Off accordingly to the event arguments.
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="args"> Event arguments </param>
        /// <remarks>
        ///  Same recipe as in the Volume_Moved method. We don't need to set value to LED, it is done automatically by synchronizer.
        /// </remarks>
        protected void FootswitchSwitched(object sender, FootswitchSwitchedEventArgs args)
        {
            this.switchedOn = args.On;

            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(footswitchKey, switchedOn);
            if (SettingsChanged != null)
                this.SettingsChanged(this, changeArgs);
        }

        #endregion

        #region main_work
        /* Effect method for processing input signal 
         * Note that we don't override Reset method. This class doesn't havy any buffers or anything similar to reset. */

        /// <summary>
        ///  Effects input signal.
        /// </summary>
        /// <param name="data"> Input signal </param>
        /// <returns> Effected output signal </returns>
        /// <remarks>
        ///  <para>
        ///   This is the place where the main work of the effect is done.
        ///   Array containing part of input signal is passed as the argument. Length of this array is equal to the value previously set to DataSize property.
        ///   This array contains samples that have values from interval [-1;1], so it is independent from format of input file.
        ///  </para>
        ///  <para>
        ///   In this effect we just run through the input array and every sample multiply by volume level.
        ///  </para>
        /// </remarks>
        public override double[] effect(double[] data)
        {
            if (switchedOn)
            {
                double[] retData = new double[data.Length];

                for (int i = 0; i < data.Length; i++)
                {
                    retData[i] = volume * data[i];
                }
                return retData;
            }

            return data;
        }

        #endregion

        #region settings
        // Getting and setting the settings of volume effect

        /// <summary>
        ///  Raised when any parameter of effect is changed.
        /// </summary>
        /// <remarks> 
        ///  We raise this event whenewer user changes any control (Volume pot or footswitch).
        /// </remarks>
        public override event EffectSettingsChangedDelegate SettingsChanged;

        /// <summary>
        ///  Sets settings 
        /// </summary>
        /// <param name="e"> Settings that are to be changed </param>
        /// <remarks>
        ///  <para>
        ///   Arguments of this method is same event args as in SettingsChanged event. 
        ///   According to the key from this argument we adjust the parameter and potentiometer. 
        ///   Note that we use SetValue method of Pot, because it doesn't raises the event.
        ///  </para>
        /// </remarks>
        public override void SetSetting(EffectSettingsChangedEventArgs e)
        {
            // all keys in this class are integers
            int key = (int)e.Key;

            // is it volume settings?
            if (key == volumeKey)
            {
                volume = (double)e.Value;
                VolumePot.SetValue(volume);
                return;
            }

            // is it footswitch settings?
            if (key == footswitchKey)
            {
                switchedOn = (bool)e.Value;
                synchronizer.SetValue(switchedOn);
                return;
            }
        }

        /// <summary>
        ///  Gets all current settings.
        /// </summary>
        /// <returns> All current settings </returns>
        /// <remarks>
        ///  We return array that contains all current settings of the effect. In our case it means, volume level and footswitch state.
        ///  Note the order of parameters in the array is the same as is returned by GetParameters method.
        /// </remarks>
        public override object[] GetAllSettings()
        {
            object[] settings = new object[2];
            settings[0] = switchedOn;
            settings[1] = volume;
            return settings;
        }

        /// <summary>
        ///  Sets all settings.
        /// </summary>
        /// <param name="settings"> Settings to set. This argument should be taken from GetAllSettings previously </param>
        /// <remarks>
        ///  We set all parameters. Passed array has the same structure as the one returned by GetAllSettings method.
        /// </remarks>
        public override void SetAllSettings(object[] settings)
        {
            object[] sets = (object[])settings;

            switchedOn = (bool)sets[0];
            synchronizer.SetValue(switchedOn);

            volume = (double) sets[1];
            VolumePot.SetValue(volume);
        }


        /// <summary>
        ///  Gets array containing all effect's parameters.
        /// </summary>
        /// <returns> Array containing description fo all parameters </returns>
        /// <remarks>
        ///  In here we describe all parameters of the effect. Remember that order of them in returned array is important and same order
        ///  must be used in GetAllSettings and GetAllSettings methods.
        /// </remarks>
        public override EffectParam[] GetParameters()
        {
            // we have two parameters
            EffectParam[] parameters = new EffectParam[2];

            /* footswitch settings: we describe it as list type, with two values
             *    To which we set string and value */
            ListItem[] footswitchState = new ListItem[2];
            footswitchState[0] = new ListItem("Turn on", true);
            footswitchState[1] = new ListItem("Turn off", false);

            /*  parameters ar described by key, string displayed to the user and bounds (or list enumeration)
             * Note that for volume parameter we must pass float value to use appropriate constructor */
            parameters[0] = new EffectParam(footswitchKey, "ON/OFF", footswitchState);
            parameters[1] = new EffectParam(volumeKey, "Volume level", 0F, 1F);

            return parameters;
        }

        #endregion
    }
}
